| /* |
| * qemu/kvm integration |
| * |
| * Copyright (C) 2006-2008 Qumranet Technologies |
| * |
| * Licensed under the terms of the GNU GPL version 2 or higher. |
| */ |
| #include "config.h" |
| #include "config-host.h" |
| |
| int kvm_allowed = 1; |
| int kvm_irqchip = 1; |
| int kvm_pit = 1; |
| |
| #include <assert.h> |
| #include <string.h> |
| #include "hw/hw.h" |
| #include "sysemu.h" |
| #include "qemu-common.h" |
| #include "console.h" |
| #include "block.h" |
| #include "compatfd.h" |
| |
| #include "qemu-kvm.h" |
| #include <libkvm.h> |
| #include <pthread.h> |
| #include <sys/utsname.h> |
| #include <sys/syscall.h> |
| #include <sys/mman.h> |
| |
| #define bool _Bool |
| #define false 0 |
| #define true 1 |
| |
| extern void perror(const char *s); |
| |
| kvm_context_t kvm_context; |
| |
| extern int smp_cpus; |
| |
| pthread_mutex_t qemu_mutex = PTHREAD_MUTEX_INITIALIZER; |
| pthread_cond_t qemu_vcpu_cond = PTHREAD_COND_INITIALIZER; |
| pthread_cond_t qemu_system_cond = PTHREAD_COND_INITIALIZER; |
| pthread_cond_t qemu_pause_cond = PTHREAD_COND_INITIALIZER; |
| pthread_cond_t qemu_work_cond = PTHREAD_COND_INITIALIZER; |
| __thread struct vcpu_info *vcpu; |
| |
| static int qemu_system_ready; |
| |
| #define SIG_IPI (SIGRTMIN+4) |
| |
| struct qemu_kvm_work_item { |
| struct qemu_kvm_work_item *next; |
| void (*func)(void *data); |
| void *data; |
| bool done; |
| }; |
| |
| struct vcpu_info { |
| CPUState *env; |
| int sipi_needed; |
| int init; |
| pthread_t thread; |
| int signalled; |
| int stop; |
| int stopped; |
| int created; |
| struct qemu_kvm_work_item *queued_work_first, *queued_work_last; |
| } vcpu_info[256]; |
| |
| pthread_t io_thread; |
| static int io_thread_fd = -1; |
| static int io_thread_sigfd = -1; |
| |
| static int kvm_debug_stop_requested; |
| |
| static inline unsigned long kvm_get_thread_id(void) |
| { |
| return syscall(SYS_gettid); |
| } |
| |
| static void qemu_cond_wait(pthread_cond_t *cond) |
| { |
| CPUState *env = cpu_single_env; |
| static const struct timespec ts = { |
| .tv_sec = 0, |
| .tv_nsec = 100000, |
| }; |
| |
| pthread_cond_timedwait(cond, &qemu_mutex, &ts); |
| cpu_single_env = env; |
| } |
| |
| CPUState *qemu_kvm_cpu_env(int index) |
| { |
| return vcpu_info[index].env; |
| } |
| |
| static void sig_ipi_handler(int n) |
| { |
| } |
| |
| static void on_vcpu(CPUState *env, void (*func)(void *data), void *data) |
| { |
| struct vcpu_info *vi = &vcpu_info[env->cpu_index]; |
| struct qemu_kvm_work_item wi; |
| |
| if (vi == vcpu) { |
| func(data); |
| return; |
| } |
| |
| wi.func = func; |
| wi.data = data; |
| if (!vi->queued_work_first) |
| vi->queued_work_first = &wi; |
| else |
| vi->queued_work_last->next = &wi; |
| vi->queued_work_last = &wi; |
| wi.next = NULL; |
| wi.done = false; |
| |
| pthread_kill(vi->thread, SIG_IPI); |
| while (!wi.done) |
| qemu_cond_wait(&qemu_work_cond); |
| } |
| |
| static void inject_interrupt(void *data) |
| { |
| cpu_interrupt(vcpu->env, (int)data); |
| } |
| |
| void kvm_inject_interrupt(CPUState *env, int mask) |
| { |
| on_vcpu(env, inject_interrupt, (void *)mask); |
| } |
| |
| void kvm_update_interrupt_request(CPUState *env) |
| { |
| int signal = 0; |
| |
| if (env) { |
| if (!vcpu) |
| signal = 1; |
| if (vcpu && env != vcpu->env && !vcpu_info[env->cpu_index].signalled) |
| signal = 1; |
| |
| if (signal) { |
| vcpu_info[env->cpu_index].signalled = 1; |
| if (vcpu_info[env->cpu_index].thread) |
| pthread_kill(vcpu_info[env->cpu_index].thread, SIG_IPI); |
| } |
| } |
| } |
| |
| void kvm_update_after_sipi(CPUState *env) |
| { |
| vcpu_info[env->cpu_index].sipi_needed = 1; |
| kvm_update_interrupt_request(env); |
| } |
| |
| void kvm_apic_init(CPUState *env) |
| { |
| if (env->cpu_index != 0) |
| vcpu_info[env->cpu_index].init = 1; |
| kvm_update_interrupt_request(env); |
| } |
| |
| #include <signal.h> |
| |
| static int try_push_interrupts(void *opaque) |
| { |
| return kvm_arch_try_push_interrupts(opaque); |
| } |
| |
| static int try_push_nmi(void *opaque) |
| { |
| return kvm_arch_try_push_nmi(opaque); |
| } |
| |
| static void post_kvm_run(void *opaque, int vcpu) |
| { |
| |
| pthread_mutex_lock(&qemu_mutex); |
| kvm_arch_post_kvm_run(opaque, vcpu); |
| } |
| |
| static int pre_kvm_run(void *opaque, int vcpu) |
| { |
| CPUState *env = qemu_kvm_cpu_env(vcpu); |
| |
| kvm_arch_pre_kvm_run(opaque, vcpu); |
| |
| if (env->interrupt_request & CPU_INTERRUPT_EXIT) |
| return 1; |
| pthread_mutex_unlock(&qemu_mutex); |
| return 0; |
| } |
| |
| static void kvm_do_load_registers(void *_env) |
| { |
| CPUState *env = _env; |
| |
| kvm_arch_load_regs(env); |
| } |
| |
| void kvm_load_registers(CPUState *env) |
| { |
| if (kvm_enabled() && qemu_system_ready) |
| on_vcpu(env, kvm_do_load_registers, env); |
| } |
| |
| static void kvm_do_save_registers(void *_env) |
| { |
| CPUState *env = _env; |
| |
| kvm_arch_save_regs(env); |
| } |
| |
| void kvm_save_registers(CPUState *env) |
| { |
| if (kvm_enabled()) |
| on_vcpu(env, kvm_do_save_registers, env); |
| } |
| |
| int kvm_cpu_exec(CPUState *env) |
| { |
| int r; |
| |
| r = kvm_run(kvm_context, env->cpu_index); |
| if (r < 0) { |
| printf("kvm_run returned %d\n", r); |
| exit(1); |
| } |
| |
| return 0; |
| } |
| |
| extern int vm_running; |
| |
| static int has_work(CPUState *env) |
| { |
| if (!vm_running || (env && vcpu_info[env->cpu_index].stopped)) |
| return 0; |
| if (!env->halted) |
| return 1; |
| return kvm_arch_has_work(env); |
| } |
| |
| static void flush_queued_work(CPUState *env) |
| { |
| struct vcpu_info *vi = &vcpu_info[env->cpu_index]; |
| struct qemu_kvm_work_item *wi; |
| |
| if (!vi->queued_work_first) |
| return; |
| |
| while ((wi = vi->queued_work_first)) { |
| vi->queued_work_first = wi->next; |
| wi->func(wi->data); |
| wi->done = true; |
| } |
| vi->queued_work_last = NULL; |
| pthread_cond_broadcast(&qemu_work_cond); |
| } |
| |
| static void kvm_main_loop_wait(CPUState *env, int timeout) |
| { |
| struct timespec ts; |
| int r, e; |
| siginfo_t siginfo; |
| sigset_t waitset; |
| |
| pthread_mutex_unlock(&qemu_mutex); |
| |
| ts.tv_sec = timeout / 1000; |
| ts.tv_nsec = (timeout % 1000) * 1000000; |
| sigemptyset(&waitset); |
| sigaddset(&waitset, SIG_IPI); |
| |
| r = sigtimedwait(&waitset, &siginfo, &ts); |
| e = errno; |
| |
| pthread_mutex_lock(&qemu_mutex); |
| |
| if (r == -1 && !(e == EAGAIN || e == EINTR)) { |
| printf("sigtimedwait: %s\n", strerror(e)); |
| exit(1); |
| } |
| |
| cpu_single_env = env; |
| flush_queued_work(env); |
| |
| if (vcpu_info[env->cpu_index].stop) { |
| vcpu_info[env->cpu_index].stop = 0; |
| vcpu_info[env->cpu_index].stopped = 1; |
| pthread_cond_signal(&qemu_pause_cond); |
| } |
| |
| vcpu_info[env->cpu_index].signalled = 0; |
| } |
| |
| static int all_threads_paused(void) |
| { |
| int i; |
| |
| for (i = 0; i < smp_cpus; ++i) |
| if (vcpu_info[i].stop) |
| return 0; |
| return 1; |
| } |
| |
| static void pause_all_threads(void) |
| { |
| int i; |
| |
| assert(!cpu_single_env); |
| |
| for (i = 0; i < smp_cpus; ++i) { |
| vcpu_info[i].stop = 1; |
| pthread_kill(vcpu_info[i].thread, SIG_IPI); |
| } |
| while (!all_threads_paused()) |
| qemu_cond_wait(&qemu_pause_cond); |
| } |
| |
| static void resume_all_threads(void) |
| { |
| int i; |
| |
| assert(!cpu_single_env); |
| |
| for (i = 0; i < smp_cpus; ++i) { |
| vcpu_info[i].stop = 0; |
| vcpu_info[i].stopped = 0; |
| pthread_kill(vcpu_info[i].thread, SIG_IPI); |
| } |
| } |
| |
| static void kvm_vm_state_change_handler(void *context, int running) |
| { |
| if (running) |
| resume_all_threads(); |
| else |
| pause_all_threads(); |
| } |
| |
| static void update_regs_for_sipi(CPUState *env) |
| { |
| kvm_arch_update_regs_for_sipi(env); |
| vcpu_info[env->cpu_index].sipi_needed = 0; |
| } |
| |
| static void update_regs_for_init(CPUState *env) |
| { |
| #ifdef TARGET_I386 |
| SegmentCache cs = env->segs[R_CS]; |
| #endif |
| |
| cpu_reset(env); |
| |
| #ifdef TARGET_I386 |
| /* restore SIPI vector */ |
| if(vcpu_info[env->cpu_index].sipi_needed) |
| env->segs[R_CS] = cs; |
| |
| vcpu_info[env->cpu_index].init = 0; |
| #endif |
| kvm_arch_load_regs(env); |
| } |
| |
| static void setup_kernel_sigmask(CPUState *env) |
| { |
| sigset_t set; |
| |
| sigemptyset(&set); |
| sigaddset(&set, SIGUSR2); |
| sigaddset(&set, SIGIO); |
| sigaddset(&set, SIGALRM); |
| sigprocmask(SIG_BLOCK, &set, NULL); |
| |
| sigprocmask(SIG_BLOCK, NULL, &set); |
| sigdelset(&set, SIG_IPI); |
| |
| kvm_set_signal_mask(kvm_context, env->cpu_index, &set); |
| } |
| |
| void qemu_kvm_system_reset(void) |
| { |
| int i; |
| |
| pause_all_threads(); |
| |
| qemu_system_reset(); |
| |
| for (i = 0; i < smp_cpus; ++i) |
| kvm_arch_cpu_reset(vcpu_info[i].env); |
| |
| resume_all_threads(); |
| } |
| |
| static int kvm_main_loop_cpu(CPUState *env) |
| { |
| struct vcpu_info *info = &vcpu_info[env->cpu_index]; |
| |
| setup_kernel_sigmask(env); |
| |
| pthread_mutex_lock(&qemu_mutex); |
| if (kvm_irqchip_in_kernel(kvm_context)) |
| env->halted = 0; |
| |
| kvm_qemu_init_env(env); |
| #ifdef TARGET_I386 |
| kvm_tpr_vcpu_start(env); |
| #endif |
| |
| cpu_single_env = env; |
| kvm_load_registers(env); |
| |
| while (1) { |
| while (!has_work(env)) |
| kvm_main_loop_wait(env, 1000); |
| if (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) |
| env->halted = 0; |
| if (!kvm_irqchip_in_kernel(kvm_context)) { |
| if (info->init) |
| update_regs_for_init(env); |
| if (info->sipi_needed) |
| update_regs_for_sipi(env); |
| } |
| if (!env->halted && !info->init) |
| kvm_cpu_exec(env); |
| env->interrupt_request &= ~CPU_INTERRUPT_EXIT; |
| kvm_main_loop_wait(env, 0); |
| } |
| pthread_mutex_unlock(&qemu_mutex); |
| return 0; |
| } |
| |
| static void *ap_main_loop(void *_env) |
| { |
| CPUState *env = _env; |
| sigset_t signals; |
| |
| vcpu = &vcpu_info[env->cpu_index]; |
| vcpu->env = env; |
| vcpu->env->thread_id = kvm_get_thread_id(); |
| sigfillset(&signals); |
| sigprocmask(SIG_BLOCK, &signals, NULL); |
| kvm_create_vcpu(kvm_context, env->cpu_index); |
| kvm_qemu_init_env(env); |
| |
| /* signal VCPU creation */ |
| pthread_mutex_lock(&qemu_mutex); |
| vcpu->created = 1; |
| pthread_cond_signal(&qemu_vcpu_cond); |
| |
| /* and wait for machine initialization */ |
| while (!qemu_system_ready) |
| qemu_cond_wait(&qemu_system_cond); |
| pthread_mutex_unlock(&qemu_mutex); |
| |
| kvm_main_loop_cpu(env); |
| return NULL; |
| } |
| |
| void kvm_init_new_ap(int cpu, CPUState *env) |
| { |
| pthread_create(&vcpu_info[cpu].thread, NULL, ap_main_loop, env); |
| |
| while (vcpu_info[cpu].created == 0) |
| qemu_cond_wait(&qemu_vcpu_cond); |
| } |
| |
| int kvm_init_ap(void) |
| { |
| #ifdef TARGET_I386 |
| kvm_tpr_opt_setup(); |
| #endif |
| qemu_add_vm_change_state_handler(kvm_vm_state_change_handler, NULL); |
| |
| signal(SIG_IPI, sig_ipi_handler); |
| return 0; |
| } |
| |
| void qemu_kvm_notify_work(void) |
| { |
| uint64_t value = 1; |
| char buffer[8]; |
| size_t offset = 0; |
| |
| if (io_thread_fd == -1) |
| return; |
| |
| memcpy(buffer, &value, sizeof(value)); |
| |
| while (offset < 8) { |
| ssize_t len; |
| |
| len = write(io_thread_fd, buffer + offset, 8 - offset); |
| if (len == -1 && errno == EINTR) |
| continue; |
| |
| if (len <= 0) |
| break; |
| |
| offset += len; |
| } |
| |
| if (offset != 8) |
| fprintf(stderr, "failed to notify io thread\n"); |
| } |
| |
| /* If we have signalfd, we mask out the signals we want to handle and then |
| * use signalfd to listen for them. We rely on whatever the current signal |
| * handler is to dispatch the signals when we receive them. |
| */ |
| |
| static void sigfd_handler(void *opaque) |
| { |
| int fd = (unsigned long)opaque; |
| struct qemu_signalfd_siginfo info; |
| struct sigaction action; |
| ssize_t len; |
| |
| while (1) { |
| do { |
| len = read(fd, &info, sizeof(info)); |
| } while (len == -1 && errno == EINTR); |
| |
| if (len == -1 && errno == EAGAIN) |
| break; |
| |
| if (len != sizeof(info)) { |
| printf("read from sigfd returned %ld: %m\n", len); |
| return; |
| } |
| |
| sigaction(info.ssi_signo, NULL, &action); |
| if (action.sa_handler) |
| action.sa_handler(info.ssi_signo); |
| |
| } |
| } |
| |
| /* Used to break IO thread out of select */ |
| static void io_thread_wakeup(void *opaque) |
| { |
| int fd = (unsigned long)opaque; |
| char buffer[8]; |
| size_t offset = 0; |
| |
| while (offset < 8) { |
| ssize_t len; |
| |
| len = read(fd, buffer + offset, 8 - offset); |
| if (len == -1 && errno == EINTR) |
| continue; |
| |
| if (len <= 0) |
| break; |
| |
| offset += len; |
| } |
| } |
| |
| int kvm_main_loop(void) |
| { |
| int fds[2]; |
| sigset_t mask; |
| int sigfd; |
| |
| io_thread = pthread_self(); |
| qemu_system_ready = 1; |
| |
| if (qemu_eventfd(fds) == -1) { |
| fprintf(stderr, "failed to create eventfd\n"); |
| return -errno; |
| } |
| |
| qemu_set_fd_handler2(fds[0], NULL, io_thread_wakeup, NULL, |
| (void *)(unsigned long)fds[0]); |
| |
| io_thread_fd = fds[1]; |
| |
| sigemptyset(&mask); |
| sigaddset(&mask, SIGIO); |
| sigaddset(&mask, SIGALRM); |
| sigprocmask(SIG_BLOCK, &mask, NULL); |
| |
| sigfd = qemu_signalfd(&mask); |
| if (sigfd == -1) { |
| fprintf(stderr, "failed to create signalfd\n"); |
| return -errno; |
| } |
| |
| fcntl(sigfd, F_SETFL, O_NONBLOCK); |
| |
| qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL, |
| (void *)(unsigned long)sigfd); |
| |
| pthread_cond_broadcast(&qemu_system_cond); |
| |
| io_thread_sigfd = sigfd; |
| cpu_single_env = NULL; |
| |
| while (1) { |
| main_loop_wait(1000); |
| if (qemu_shutdown_requested()) |
| break; |
| else if (qemu_powerdown_requested()) |
| qemu_system_powerdown(); |
| else if (qemu_reset_requested()) |
| qemu_kvm_system_reset(); |
| else if (kvm_debug_stop_requested) { |
| vm_stop(EXCP_DEBUG); |
| kvm_debug_stop_requested = 0; |
| } |
| } |
| |
| pause_all_threads(); |
| pthread_mutex_unlock(&qemu_mutex); |
| |
| return 0; |
| } |
| |
| static int kvm_debug(void *opaque, int vcpu) |
| { |
| kvm_debug_stop_requested = 1; |
| vcpu_info[vcpu].stopped = 1; |
| return 1; |
| } |
| |
| static int kvm_inb(void *opaque, uint16_t addr, uint8_t *data) |
| { |
| *data = cpu_inb(0, addr); |
| return 0; |
| } |
| |
| static int kvm_inw(void *opaque, uint16_t addr, uint16_t *data) |
| { |
| *data = cpu_inw(0, addr); |
| return 0; |
| } |
| |
| static int kvm_inl(void *opaque, uint16_t addr, uint32_t *data) |
| { |
| *data = cpu_inl(0, addr); |
| return 0; |
| } |
| |
| #define PM_IO_BASE 0xb000 |
| |
| static int kvm_outb(void *opaque, uint16_t addr, uint8_t data) |
| { |
| if (addr == 0xb2) { |
| switch (data) { |
| case 0: { |
| cpu_outb(0, 0xb3, 0); |
| break; |
| } |
| case 0xf0: { |
| unsigned x; |
| |
| /* enable acpi */ |
| x = cpu_inw(0, PM_IO_BASE + 4); |
| x &= ~1; |
| cpu_outw(0, PM_IO_BASE + 4, x); |
| break; |
| } |
| case 0xf1: { |
| unsigned x; |
| |
| /* enable acpi */ |
| x = cpu_inw(0, PM_IO_BASE + 4); |
| x |= 1; |
| cpu_outw(0, PM_IO_BASE + 4, x); |
| break; |
| } |
| default: |
| break; |
| } |
| return 0; |
| } |
| cpu_outb(0, addr, data); |
| return 0; |
| } |
| |
| static int kvm_outw(void *opaque, uint16_t addr, uint16_t data) |
| { |
| cpu_outw(0, addr, data); |
| return 0; |
| } |
| |
| static int kvm_outl(void *opaque, uint16_t addr, uint32_t data) |
| { |
| cpu_outl(0, addr, data); |
| return 0; |
| } |
| |
| static int kvm_mmio_read(void *opaque, uint64_t addr, uint8_t *data, int len) |
| { |
| cpu_physical_memory_rw(addr, data, len, 0); |
| return 0; |
| } |
| |
| static int kvm_mmio_write(void *opaque, uint64_t addr, uint8_t *data, int len) |
| { |
| cpu_physical_memory_rw(addr, data, len, 1); |
| return 0; |
| } |
| |
| static int kvm_io_window(void *opaque) |
| { |
| return 1; |
| } |
| |
| |
| static int kvm_halt(void *opaque, int vcpu) |
| { |
| return kvm_arch_halt(opaque, vcpu); |
| } |
| |
| static int kvm_shutdown(void *opaque, int vcpu) |
| { |
| /* stop the current vcpu from going back to guest mode */ |
| vcpu_info[cpu_single_env->cpu_index].stopped = 1; |
| |
| qemu_system_reset_request(); |
| return 1; |
| } |
| |
| static struct kvm_callbacks qemu_kvm_ops = { |
| .debug = kvm_debug, |
| .inb = kvm_inb, |
| .inw = kvm_inw, |
| .inl = kvm_inl, |
| .outb = kvm_outb, |
| .outw = kvm_outw, |
| .outl = kvm_outl, |
| .mmio_read = kvm_mmio_read, |
| .mmio_write = kvm_mmio_write, |
| .halt = kvm_halt, |
| .shutdown = kvm_shutdown, |
| .io_window = kvm_io_window, |
| .try_push_interrupts = try_push_interrupts, |
| .try_push_nmi = try_push_nmi, |
| .post_kvm_run = post_kvm_run, |
| .pre_kvm_run = pre_kvm_run, |
| #ifdef TARGET_I386 |
| .tpr_access = handle_tpr_access, |
| #endif |
| #ifdef TARGET_PPC |
| .powerpc_dcr_read = handle_powerpc_dcr_read, |
| .powerpc_dcr_write = handle_powerpc_dcr_write, |
| #endif |
| }; |
| |
| int kvm_qemu_init() |
| { |
| /* Try to initialize kvm */ |
| kvm_context = kvm_init(&qemu_kvm_ops, cpu_single_env); |
| if (!kvm_context) { |
| return -1; |
| } |
| pthread_mutex_lock(&qemu_mutex); |
| |
| return 0; |
| } |
| |
| int kvm_qemu_create_context(void) |
| { |
| int r; |
| if (!kvm_irqchip) { |
| kvm_disable_irqchip_creation(kvm_context); |
| } |
| if (!kvm_pit) { |
| kvm_disable_pit_creation(kvm_context); |
| } |
| if (kvm_create(kvm_context, phys_ram_size, (void**)&phys_ram_base) < 0) { |
| kvm_qemu_destroy(); |
| return -1; |
| } |
| r = kvm_arch_qemu_create_context(); |
| if(r <0) |
| kvm_qemu_destroy(); |
| return 0; |
| } |
| |
| void kvm_qemu_destroy(void) |
| { |
| kvm_finalize(kvm_context); |
| } |
| |
| void kvm_cpu_register_physical_memory(target_phys_addr_t start_addr, |
| unsigned long size, |
| unsigned long phys_offset) |
| { |
| int r = 0; |
| unsigned long area_flags = phys_offset & ~TARGET_PAGE_MASK; |
| |
| phys_offset &= ~IO_MEM_ROM; |
| |
| if (area_flags == IO_MEM_UNASSIGNED) { |
| kvm_unregister_memory_area(kvm_context, start_addr, size); |
| return; |
| } |
| |
| r = kvm_is_containing_region(kvm_context, start_addr, size); |
| if (r) |
| return; |
| |
| if (area_flags >= TLB_MMIO) |
| return; |
| |
| r = kvm_register_phys_mem(kvm_context, start_addr, |
| phys_ram_base + phys_offset, |
| size, 0); |
| if (r < 0) { |
| printf("kvm_cpu_register_physical_memory: failed\n"); |
| exit(1); |
| } |
| return; |
| } |
| |
| void kvm_cpu_unregister_physical_memory(target_phys_addr_t start_addr, |
| target_phys_addr_t size, |
| unsigned long phys_offset) |
| { |
| kvm_unregister_memory_area(kvm_context, start_addr, size); |
| } |
| |
| int kvm_setup_guest_memory(void *area, unsigned long size) |
| { |
| int ret = 0; |
| |
| #ifdef MADV_DONTFORK |
| if (kvm_enabled() && !kvm_has_sync_mmu(kvm_context)) |
| ret = madvise(area, size, MADV_DONTFORK); |
| #endif |
| |
| if (ret) |
| perror ("madvise"); |
| |
| return ret; |
| } |
| |
| int kvm_qemu_check_extension(int ext) |
| { |
| return kvm_check_extension(kvm_context, ext); |
| } |
| |
| int kvm_qemu_init_env(CPUState *cenv) |
| { |
| return kvm_arch_qemu_init_env(cenv); |
| } |
| |
| struct kvm_guest_debug_data { |
| struct kvm_debug_guest dbg; |
| int err; |
| }; |
| |
| void kvm_invoke_guest_debug(void *data) |
| { |
| struct kvm_guest_debug_data *dbg_data = data; |
| |
| dbg_data->err = kvm_guest_debug(kvm_context, cpu_single_env->cpu_index, |
| &dbg_data->dbg); |
| } |
| |
| int kvm_update_debugger(CPUState *env) |
| { |
| struct kvm_guest_debug_data data; |
| int i; |
| |
| memset(data.dbg.breakpoints, 0, sizeof(data.dbg.breakpoints)); |
| |
| data.dbg.enabled = 0; |
| if (env->nb_breakpoints || env->singlestep_enabled) { |
| data.dbg.enabled = 1; |
| for (i = 0; i < 4 && i < env->nb_breakpoints; ++i) { |
| data.dbg.breakpoints[i].enabled = 1; |
| data.dbg.breakpoints[i].address = env->breakpoints[i]; |
| } |
| data.dbg.singlestep = env->singlestep_enabled; |
| } |
| on_vcpu(env, kvm_invoke_guest_debug, &data); |
| return data.err; |
| } |
| |
| |
| /* |
| * dirty pages logging |
| */ |
| /* FIXME: use unsigned long pointer instead of unsigned char */ |
| unsigned char *kvm_dirty_bitmap = NULL; |
| int kvm_physical_memory_set_dirty_tracking(int enable) |
| { |
| int r = 0; |
| |
| if (!kvm_enabled()) |
| return 0; |
| |
| if (enable) { |
| if (!kvm_dirty_bitmap) { |
| unsigned bitmap_size = BITMAP_SIZE(phys_ram_size); |
| kvm_dirty_bitmap = qemu_malloc(bitmap_size); |
| if (kvm_dirty_bitmap == NULL) { |
| perror("Failed to allocate dirty pages bitmap"); |
| r=-1; |
| } |
| else { |
| r = kvm_dirty_pages_log_enable_all(kvm_context); |
| } |
| } |
| } |
| else { |
| if (kvm_dirty_bitmap) { |
| r = kvm_dirty_pages_log_reset(kvm_context); |
| qemu_free(kvm_dirty_bitmap); |
| kvm_dirty_bitmap = NULL; |
| } |
| } |
| return r; |
| } |
| |
| /* get kvm's dirty pages bitmap and update qemu's */ |
| int kvm_get_dirty_pages_log_range(unsigned long start_addr, |
| unsigned char *bitmap, |
| unsigned int offset, |
| unsigned long mem_size) |
| { |
| unsigned int i, j, n=0; |
| unsigned char c; |
| unsigned page_number, addr, addr1; |
| unsigned int len = ((mem_size/TARGET_PAGE_SIZE) + 7) / 8; |
| |
| /* |
| * bitmap-traveling is faster than memory-traveling (for addr...) |
| * especially when most of the memory is not dirty. |
| */ |
| for (i=0; i<len; i++) { |
| c = bitmap[i]; |
| while (c>0) { |
| j = ffsl(c) - 1; |
| c &= ~(1u<<j); |
| page_number = i * 8 + j; |
| addr1 = page_number * TARGET_PAGE_SIZE; |
| addr = offset + addr1; |
| cpu_physical_memory_set_dirty(addr); |
| n++; |
| } |
| } |
| return 0; |
| } |
| int kvm_get_dirty_bitmap_cb(unsigned long start, unsigned long len, |
| void *bitmap, void *opaque) |
| { |
| return kvm_get_dirty_pages_log_range(start, bitmap, start, len); |
| } |
| |
| /* |
| * get kvm's dirty pages bitmap and update qemu's |
| * we only care about physical ram, which resides in slots 0 and 3 |
| */ |
| int kvm_update_dirty_pages_log(void) |
| { |
| int r = 0; |
| |
| |
| r = kvm_get_dirty_pages_range(kvm_context, 0, phys_ram_size, |
| kvm_dirty_bitmap, NULL, |
| kvm_get_dirty_bitmap_cb); |
| return r; |
| } |
| |
| void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, |
| int log) |
| { |
| if (log) |
| kvm_dirty_pages_log_enable_slot(kvm_context, start, size); |
| else |
| kvm_dirty_pages_log_disable_slot(kvm_context, start, size); |
| } |
| |
| int kvm_get_phys_ram_page_bitmap(unsigned char *bitmap) |
| { |
| unsigned int bsize = BITMAP_SIZE(phys_ram_size); |
| unsigned int brsize = BITMAP_SIZE(ram_size); |
| unsigned int extra_pages = (phys_ram_size - ram_size) / TARGET_PAGE_SIZE; |
| unsigned int extra_bytes = (extra_pages +7)/8; |
| unsigned int hole_start = BITMAP_SIZE(0xa0000); |
| unsigned int hole_end = BITMAP_SIZE(0xc0000); |
| |
| memset(bitmap, 0xFF, brsize + extra_bytes); |
| memset(bitmap + hole_start, 0, hole_end - hole_start); |
| memset(bitmap + brsize + extra_bytes, 0, bsize - brsize - extra_bytes); |
| |
| return 0; |
| } |
| |
| #ifdef KVM_CAP_IRQCHIP |
| |
| int kvm_set_irq(int irq, int level) |
| { |
| return kvm_set_irq_level(kvm_context, irq, level); |
| } |
| |
| #endif |
| |
| int qemu_kvm_get_dirty_pages(unsigned long phys_addr, void *buf) |
| { |
| return kvm_get_dirty_pages(kvm_context, phys_addr, buf); |
| } |
| |
| void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr, |
| unsigned long size, int log, int writable) |
| { |
| return kvm_create_phys_mem(kvm_context, start_addr, size, log, writable); |
| } |
| |
| void kvm_cpu_destroy_phys_mem(target_phys_addr_t start_addr, |
| unsigned long size) |
| { |
| kvm_destroy_phys_mem(kvm_context, start_addr, size); |
| } |
| |
| void kvm_mutex_unlock(void) |
| { |
| assert(!cpu_single_env); |
| pthread_mutex_unlock(&qemu_mutex); |
| } |
| |
| void kvm_mutex_lock(void) |
| { |
| pthread_mutex_lock(&qemu_mutex); |
| cpu_single_env = NULL; |
| } |
| |
| int qemu_kvm_register_coalesced_mmio(target_phys_addr_t addr, unsigned int size) |
| { |
| return kvm_register_coalesced_mmio(kvm_context, addr, size); |
| } |
| |
| int qemu_kvm_unregister_coalesced_mmio(target_phys_addr_t addr, |
| unsigned int size) |
| { |
| return kvm_unregister_coalesced_mmio(kvm_context, addr, size); |
| } |