#include "qemu-common.h"
#include "sysemu.h"
+#include "hw/hw.h"
#include "gdbstub.h"
#include "kvm.h"
int fd;
int vmfd;
int coalesced_mmio;
+ int broken_set_mem_region;
+ int migration_log;
#ifdef KVM_CAP_SET_GUEST_DEBUG
struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
#endif
mem.memory_size = slot->memory_size;
mem.userspace_addr = (unsigned long)qemu_get_ram_ptr(slot->phys_offset);
mem.flags = slot->flags;
-
+ if (s->migration_log) {
+ mem.flags |= KVM_MEM_LOG_DIRTY_PAGES;
+ }
return kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
}
return ret;
}
+int kvm_put_mp_state(CPUState *env)
+{
+ struct kvm_mp_state mp_state = { .mp_state = env->mp_state };
+
+ return kvm_vcpu_ioctl(env, KVM_SET_MP_STATE, &mp_state);
+}
+
+int kvm_get_mp_state(CPUState *env)
+{
+ struct kvm_mp_state mp_state;
+ int ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_MP_STATE, &mp_state);
+ if (ret < 0) {
+ return ret;
+ }
+ env->mp_state = mp_state.mp_state;
+ return 0;
+}
+
int kvm_sync_vcpus(void)
{
CPUState *env;
* dirty pages logging control
*/
static int kvm_dirty_pages_log_change(target_phys_addr_t phys_addr,
- ram_addr_t size, unsigned flags,
- unsigned mask)
+ ram_addr_t size, int flags, int mask)
{
KVMState *s = kvm_state;
KVMSlot *mem = kvm_lookup_matching_slot(s, phys_addr, phys_addr + size);
+ int old_flags;
+
if (mem == NULL) {
fprintf(stderr, "BUG: %s: invalid parameters " TARGET_FMT_plx "-"
TARGET_FMT_plx "\n", __func__, phys_addr,
return -EINVAL;
}
- flags = (mem->flags & ~mask) | flags;
- /* Nothing changed, no need to issue ioctl */
- if (flags == mem->flags)
- return 0;
+ old_flags = mem->flags;
+ flags = (mem->flags & ~mask) | flags;
mem->flags = flags;
+ /* If nothing changed effectively, no need to issue ioctl */
+ if (s->migration_log) {
+ flags |= KVM_MEM_LOG_DIRTY_PAGES;
+ }
+ if (flags == old_flags) {
+ return 0;
+ }
+
return kvm_set_user_memory_region(s, mem);
}
KVM_MEM_LOG_DIRTY_PAGES);
}
+int kvm_set_migration_log(int enable)
+{
+ KVMState *s = kvm_state;
+ KVMSlot *mem;
+ int i, err;
+
+ s->migration_log = enable;
+
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ mem = &s->slots[i];
+
+ if (!!(mem->flags & KVM_MEM_LOG_DIRTY_PAGES) == enable) {
+ continue;
+ }
+ err = kvm_set_user_memory_region(s, mem);
+ if (err) {
+ return err;
+ }
+ }
+ return 0;
+}
+
/**
* kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space
* This function updates qemu's dirty bitmap using cpu_physical_memory_set_dirty().
* @start_add: start of logged region.
* @end_addr: end of logged region.
*/
-void kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
- target_phys_addr_t end_addr)
+int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr)
{
KVMState *s = kvm_state;
- KVMDirtyLog d;
- KVMSlot *mem = kvm_lookup_matching_slot(s, start_addr, end_addr);
- unsigned long alloc_size;
+ unsigned long size, allocated_size = 0;
+ target_phys_addr_t phys_addr;
ram_addr_t addr;
- target_phys_addr_t phys_addr = start_addr;
+ KVMDirtyLog d;
+ KVMSlot *mem;
+ int ret = 0;
- dprintf("sync addr: " TARGET_FMT_lx " into %lx\n", start_addr,
- mem->phys_offset);
- if (mem == NULL) {
- fprintf(stderr, "BUG: %s: invalid parameters " TARGET_FMT_plx "-"
- TARGET_FMT_plx "\n", __func__, phys_addr, end_addr - 1);
- return;
- }
+ d.dirty_bitmap = NULL;
+ while (start_addr < end_addr) {
+ mem = kvm_lookup_overlapping_slot(s, start_addr, end_addr);
+ if (mem == NULL) {
+ break;
+ }
- alloc_size = mem->memory_size >> TARGET_PAGE_BITS / sizeof(d.dirty_bitmap);
- d.dirty_bitmap = qemu_mallocz(alloc_size);
+ size = ((mem->memory_size >> TARGET_PAGE_BITS) + 7) / 8;
+ if (!d.dirty_bitmap) {
+ d.dirty_bitmap = qemu_malloc(size);
+ } else if (size > allocated_size) {
+ d.dirty_bitmap = qemu_realloc(d.dirty_bitmap, size);
+ }
+ allocated_size = size;
+ memset(d.dirty_bitmap, 0, allocated_size);
- d.slot = mem->slot;
- dprintf("slot %d, phys_addr %llx, uaddr: %llx\n",
- d.slot, mem->start_addr, mem->phys_offset);
+ d.slot = mem->slot;
- if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
- dprintf("ioctl failed %d\n", errno);
- goto out;
- }
+ if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
+ dprintf("ioctl failed %d\n", errno);
+ ret = -1;
+ break;
+ }
+
+ for (phys_addr = mem->start_addr, addr = mem->phys_offset;
+ phys_addr < mem->start_addr + mem->memory_size;
+ phys_addr += TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) {
+ unsigned long *bitmap = (unsigned long *)d.dirty_bitmap;
+ unsigned nr = (phys_addr - mem->start_addr) >> TARGET_PAGE_BITS;
+ unsigned word = nr / (sizeof(*bitmap) * 8);
+ unsigned bit = nr % (sizeof(*bitmap) * 8);
- phys_addr = start_addr;
- for (addr = mem->phys_offset; phys_addr < end_addr; phys_addr+= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) {
- unsigned long *bitmap = (unsigned long *)d.dirty_bitmap;
- unsigned nr = (phys_addr - start_addr) >> TARGET_PAGE_BITS;
- unsigned word = nr / (sizeof(*bitmap) * 8);
- unsigned bit = nr % (sizeof(*bitmap) * 8);
- if ((bitmap[word] >> bit) & 1)
- cpu_physical_memory_set_dirty(addr);
+ if ((bitmap[word] >> bit) & 1) {
+ cpu_physical_memory_set_dirty(addr);
+ }
+ }
+ start_addr = phys_addr;
}
-out:
qemu_free(d.dirty_bitmap);
+
+ return ret;
}
int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size)
return ret;
}
+int kvm_check_extension(KVMState *s, unsigned int extension)
+{
+ int ret;
+
+ ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, extension);
+ if (ret < 0) {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void kvm_reset_vcpus(void *opaque)
+{
+ kvm_sync_vcpus();
+}
+
int kvm_init(int smp_cpus)
{
+ static const char upgrade_note[] =
+ "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
+ "(see http://sourceforge.net/projects/kvm).\n";
KVMState *s;
int ret;
int i;
- if (smp_cpus > 1)
+ if (smp_cpus > 1) {
+ fprintf(stderr, "No SMP KVM support, use '-smp 1'\n");
return -EINVAL;
+ }
s = qemu_mallocz(sizeof(KVMState));
* just use a user allocated buffer so we can use regular pages
* unmodified. Make sure we have a sufficiently modern version of KVM.
*/
- ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);
- if (ret <= 0) {
- if (ret == 0)
- ret = -EINVAL;
- fprintf(stderr, "kvm does not support KVM_CAP_USER_MEMORY\n");
+ if (!kvm_check_extension(s, KVM_CAP_USER_MEMORY)) {
+ ret = -EINVAL;
+ fprintf(stderr, "kvm does not support KVM_CAP_USER_MEMORY\n%s",
+ upgrade_note);
goto err;
}
/* There was a nasty bug in < kvm-80 that prevents memory slots from being
* destroyed properly. Since we rely on this capability, refuse to work
* with any kernel without this capability. */
- ret = kvm_ioctl(s, KVM_CHECK_EXTENSION,
- KVM_CAP_DESTROY_MEMORY_REGION_WORKS);
- if (ret <= 0) {
- if (ret == 0)
- ret = -EINVAL;
+ if (!kvm_check_extension(s, KVM_CAP_DESTROY_MEMORY_REGION_WORKS)) {
+ ret = -EINVAL;
fprintf(stderr,
- "KVM kernel module broken (DESTROY_MEMORY_REGION)\n"
- "Please upgrade to at least kvm-81.\n");
+ "KVM kernel module broken (DESTROY_MEMORY_REGION).\n%s",
+ upgrade_note);
goto err;
}
- s->coalesced_mmio = 0;
#ifdef KVM_CAP_COALESCED_MMIO
- ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO);
- if (ret > 0)
- s->coalesced_mmio = ret;
+ s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
+#else
+ s->coalesced_mmio = 0;
+#endif
+
+ s->broken_set_mem_region = 1;
+#ifdef KVM_CAP_JOIN_MEMORY_REGIONS_WORKS
+ ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_JOIN_MEMORY_REGIONS_WORKS);
+ if (ret > 0) {
+ s->broken_set_mem_region = 0;
+ }
#endif
ret = kvm_arch_init(s, smp_cpus);
if (ret < 0)
goto err;
+ qemu_register_reset(kvm_reset_vcpus, INT_MAX, NULL);
+
kvm_state = s;
return 0;
* address as the first existing one. If not or if some overlapping
* slot comes around later, we will fail (not seen in practice so far)
* - and actually require a recent KVM version. */
- if (old.start_addr == start_addr && old.memory_size < size &&
+ if (s->broken_set_mem_region &&
+ old.start_addr == start_addr && old.memory_size < size &&
flags < IO_MEM_UNASSIGNED) {
mem = kvm_alloc_slot(s);
mem->memory_size = old.memory_size;
#ifdef KVM_CAP_SYNC_MMU
KVMState *s = kvm_state;
- if (kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_SYNC_MMU) > 0)
- return 1;
-#endif
-
+ return kvm_check_extension(s, KVM_CAP_SYNC_MMU);
+#else
return 0;
+#endif
}
void kvm_setup_guest_memory(void *start, size_t size)