diff options
Diffstat (limited to 'kvm-all.c')
| -rw-r--r-- | kvm-all.c | 190 |
1 files changed, 152 insertions, 38 deletions
diff --git a/kvm-all.c b/kvm-all.c index c4babdac0d..ba2cee10f2 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -28,13 +28,14 @@ #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 #include <sys/eventfd.h> #endif -/* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ +/* KVM uses PAGE_SIZE in its definition of COALESCED_MMIO_MAX */ #define PAGE_SIZE TARGET_PAGE_SIZE //#define DEBUG_KVM @@ -75,9 +76,13 @@ struct KVMState struct kvm_sw_breakpoint_head kvm_sw_breakpoints; #endif int pit_in_kernel; + int pit_state2; int xsave, xcrs; int many_ioeventfds; - int irqchip_inject_ioctl; + /* The man page (and posix) say ioctl numbers are signed int, but + * they're not. Linux, glibc and *BSD all treat ioctl numbers as + * unsigned, and treating them as signed here can break things */ + unsigned irqchip_inject_ioctl; #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing *irq_routes; int nr_allocated_irq_routes; @@ -188,7 +193,7 @@ static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) static void kvm_reset_vcpu(void *opaque) { - CPUState *env = opaque; + CPUArchState *env = opaque; kvm_arch_reset_vcpu(env); } @@ -198,7 +203,7 @@ int kvm_pit_in_kernel(void) return kvm_state->pit_in_kernel; } -int kvm_init_vcpu(CPUState *env) +int kvm_init_vcpu(CPUArchState *env) { KVMState *s = kvm_state; long mmap_size; @@ -446,6 +451,7 @@ int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) zone.addr = start; zone.size = size; + zone.pad = 0; ret = kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); } @@ -463,6 +469,7 @@ int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) zone.addr = start; zone.size = size; + zone.pad = 0; ret = kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); } @@ -541,17 +548,26 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add) target_phys_addr_t start_addr = section->offset_within_address_space; ram_addr_t size = section->size; void *ram = NULL; + unsigned delta; /* 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); + delta = TARGET_PAGE_ALIGN(size) - size; + if (delta > size) { + return; + } + start_addr += delta; + size -= delta; + size &= TARGET_PAGE_MASK; + if (!size || (start_addr & ~TARGET_PAGE_MASK)) { + return; + } if (!memory_region_is_ram(mr)) { return; } - ram = memory_region_get_ram_ptr(mr) + section->offset_within_region; + ram = memory_region_get_ram_ptr(mr) + section->offset_within_region + delta; while (1) { mem = kvm_lookup_overlapping_slot(s, start_addr, start_addr + size); @@ -674,6 +690,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 +710,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,17 +742,98 @@ 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) +static void kvm_handle_interrupt(CPUArchState *env, int mask) { env->interrupt_request |= mask; @@ -954,6 +1064,10 @@ int kvm_init(void) s->xcrs = kvm_check_extension(s, KVM_CAP_XCRS); #endif +#ifdef KVM_CAP_PIT_STATE2 + s->pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); +#endif + ret = kvm_arch_init(s); if (ret < 0) { goto err; @@ -965,7 +1079,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(); @@ -1024,7 +1138,7 @@ static void kvm_handle_io(uint16_t port, void *data, int direction, int size, } } -static int kvm_handle_internal_error(CPUState *env, struct kvm_run *run) +static int kvm_handle_internal_error(CPUArchState *env, struct kvm_run *run) { fprintf(stderr, "KVM internal error."); if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { @@ -1079,7 +1193,7 @@ void kvm_flush_coalesced_mmio_buffer(void) static void do_kvm_cpu_synchronize_state(void *_env) { - CPUState *env = _env; + CPUArchState *env = _env; if (!env->kvm_vcpu_dirty) { kvm_arch_get_registers(env); @@ -1087,26 +1201,26 @@ static void do_kvm_cpu_synchronize_state(void *_env) } } -void kvm_cpu_synchronize_state(CPUState *env) +void kvm_cpu_synchronize_state(CPUArchState *env) { if (!env->kvm_vcpu_dirty) { run_on_cpu(env, do_kvm_cpu_synchronize_state, env); } } -void kvm_cpu_synchronize_post_reset(CPUState *env) +void kvm_cpu_synchronize_post_reset(CPUArchState *env) { kvm_arch_put_registers(env, KVM_PUT_RESET_STATE); env->kvm_vcpu_dirty = 0; } -void kvm_cpu_synchronize_post_init(CPUState *env) +void kvm_cpu_synchronize_post_init(CPUArchState *env) { kvm_arch_put_registers(env, KVM_PUT_FULL_STATE); env->kvm_vcpu_dirty = 0; } -int kvm_cpu_exec(CPUState *env) +int kvm_cpu_exec(CPUArchState *env) { struct kvm_run *run = env->kvm_run; int ret, run_ret; @@ -1118,8 +1232,6 @@ int kvm_cpu_exec(CPUState *env) return EXCP_HLT; } - cpu_single_env = env; - do { if (env->kvm_vcpu_dirty) { kvm_arch_put_registers(env, KVM_PUT_RUNTIME_STATE); @@ -1136,13 +1248,11 @@ int kvm_cpu_exec(CPUState *env) */ qemu_cpu_kick_self(); } - cpu_single_env = NULL; qemu_mutex_unlock_iothread(); run_ret = kvm_vcpu_ioctl(env, KVM_RUN, 0); qemu_mutex_lock_iothread(); - cpu_single_env = env; kvm_arch_post_run(env, run); kvm_flush_coalesced_mmio_buffer(); @@ -1206,7 +1316,6 @@ int kvm_cpu_exec(CPUState *env) } env->exit_request = 0; - cpu_single_env = NULL; return ret; } @@ -1244,7 +1353,7 @@ int kvm_vm_ioctl(KVMState *s, int type, ...) return ret; } -int kvm_vcpu_ioctl(CPUState *env, int type, ...) +int kvm_vcpu_ioctl(CPUArchState *env, int type, ...) { int ret; void *arg; @@ -1291,6 +1400,11 @@ int kvm_has_xcrs(void) return kvm_state->xcrs; } +int kvm_has_pit_state2(void) +{ + return kvm_state->pit_state2; +} + int kvm_has_many_ioeventfds(void) { if (!kvm_enabled()) { @@ -1328,7 +1442,7 @@ void kvm_setup_guest_memory(void *start, size_t size) } #ifdef KVM_CAP_SET_GUEST_DEBUG -struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUArchState *env, target_ulong pc) { struct kvm_sw_breakpoint *bp; @@ -1341,26 +1455,26 @@ struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, return NULL; } -int kvm_sw_breakpoints_active(CPUState *env) +int kvm_sw_breakpoints_active(CPUArchState *env) { return !QTAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints); } struct kvm_set_guest_debug_data { struct kvm_guest_debug dbg; - CPUState *env; + CPUArchState *env; int err; }; static void kvm_invoke_set_guest_debug(void *data) { struct kvm_set_guest_debug_data *dbg_data = data; - CPUState *env = dbg_data->env; + CPUArchState *env = dbg_data->env; dbg_data->err = kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, &dbg_data->dbg); } -int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) +int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) { struct kvm_set_guest_debug_data data; @@ -1376,11 +1490,11 @@ int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) return data.err; } -int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, +int kvm_insert_breakpoint(CPUArchState *current_env, target_ulong addr, target_ulong len, int type) { struct kvm_sw_breakpoint *bp; - CPUState *env; + CPUArchState *env; int err; if (type == GDB_BREAKPOINT_SW) { @@ -1421,11 +1535,11 @@ int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, return 0; } -int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr, +int kvm_remove_breakpoint(CPUArchState *current_env, target_ulong addr, target_ulong len, int type) { struct kvm_sw_breakpoint *bp; - CPUState *env; + CPUArchState *env; int err; if (type == GDB_BREAKPOINT_SW) { @@ -1462,11 +1576,11 @@ int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr, return 0; } -void kvm_remove_all_breakpoints(CPUState *current_env) +void kvm_remove_all_breakpoints(CPUArchState *current_env) { struct kvm_sw_breakpoint *bp, *next; KVMState *s = current_env->kvm_state; - CPUState *env; + CPUArchState *env; QTAILQ_FOREACH_SAFE(bp, &s->kvm_sw_breakpoints, entry, next) { if (kvm_arch_remove_sw_breakpoint(current_env, bp) != 0) { @@ -1487,29 +1601,29 @@ void kvm_remove_all_breakpoints(CPUState *current_env) #else /* !KVM_CAP_SET_GUEST_DEBUG */ -int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) +int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) { return -EINVAL; } -int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, +int kvm_insert_breakpoint(CPUArchState *current_env, target_ulong addr, target_ulong len, int type) { return -EINVAL; } -int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr, +int kvm_remove_breakpoint(CPUArchState *current_env, target_ulong addr, target_ulong len, int type) { return -EINVAL; } -void kvm_remove_all_breakpoints(CPUState *current_env) +void kvm_remove_all_breakpoints(CPUArchState *current_env) { } #endif /* !KVM_CAP_SET_GUEST_DEBUG */ -int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset) +int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset) { struct kvm_signal_mask *sigmask; int r; @@ -1579,7 +1693,7 @@ int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) return 0; } -int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr) +int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr) { return kvm_arch_on_sigbus_vcpu(env, code, addr); } |