diff --git a/Makefile.target b/Makefile.target index 62168c246f..00267e7920 100644 --- a/Makefile.target +++ b/Makefile.target @@ -183,6 +183,9 @@ CFLAGS+=-I/opt/SUNWspro/prod/include/cc endif endif +kvm.o: CFLAGS+=$(KVM_CFLAGS) +kvm-all.o: CFLAGS+=$(KVM_CFLAGS) + all: $(PROGS) ######################################################### @@ -581,6 +584,9 @@ ifndef CONFIG_USER_ONLY OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o net-checksum.o OBJS+=fw_cfg.o aio.o buffered_file.o migration.o migration-tcp.o qemu-char.o OBJS+=net.o +ifdef CONFIG_KVM +OBJS+=kvm.o kvm-all.o +endif ifdef CONFIG_WIN32 OBJS+=block-raw-win32.o else diff --git a/configure b/configure index 8b0ad89b12..53167d5df7 100755 --- a/configure +++ b/configure @@ -115,6 +115,7 @@ aio="yes" nptl="yes" mixemu="no" bluez="yes" +kvm="yes" # OS specific targetos=`uname -s` @@ -303,6 +304,8 @@ for opt do ;; --disable-bluez) bluez="no" ;; + --disable-kvm) kvm="no" + ;; --enable-profiler) profiler="yes" ;; --enable-cocoa) @@ -448,6 +451,7 @@ echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-curses disable curses output" echo " --disable-bluez disable bluez stack connectivity" +echo " --disable-kvm disable KVM acceleration support" echo " --disable-nptl disable usermode NPTL support" echo " --enable-system enable all system emulation targets" echo " --disable-system disable all system emulation targets" @@ -950,6 +954,30 @@ EOF fi fi +########################################## +# kvm probe +if test "$kvm" = "yes" ; then + cat > $TMPC < +#if !defined(KVM_API_VERSION) || \ + KVM_API_VERSION < 12 || \ + KVM_API_VERSION > 12 || \ + !defined(KVM_CAP_USER_MEMORY) || \ + !defined(KVM_CAP_SET_TSS_ADDR) +#error Invalid KVM version +#endif +int main(void) { return 0; } +EOF + # FIXME make this configurable + kvm_cflags=-I/lib/modules/`uname -r`/build/include + if $cc $ARCH_CFLAGS -o $TMPE ${OS_CFLAGS} $kvm_cflags $TMPC \ + 2>/dev/null ; then + : + else + kvm="no" + fi +fi + ########################################## # AIO probe if test "$aio" = "yes" ; then @@ -1036,6 +1064,7 @@ echo "uname -r $uname_release" echo "NPTL support $nptl" echo "vde support $vde" echo "AIO support $aio" +echo "KVM support $kvm" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -1411,6 +1440,15 @@ interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"` echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h gdb_xml_files="" +# FIXME allow i386 to build on x86_64 and vice versa +if test "$kvm" = "yes" -a "$target_cpu" != "$cpu" ; then + kvm="no" +fi +# Disable KVM for linux-user +if test "$kvm" = "yes" -a "$target_softmmu" = "no" ; then + kvm="no" +fi + case "$target_cpu" in i386) echo "TARGET_ARCH=i386" >> $config_mak @@ -1420,6 +1458,11 @@ case "$target_cpu" in then echo "#define USE_KQEMU 1" >> $config_h fi + if test "$kvm" = "yes" ; then + echo "CONFIG_KVM=yes" >> $config_mak + echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak + echo "#define CONFIG_KVM" >> $config_h + fi gcc3minver=`$cc --version 2> /dev/null| fgrep "(GCC) 3." | awk '{ print $3 }' | cut -f2 -d.` if test -n "$gcc3minver" && test $gcc3minver -gt 3 then @@ -1437,6 +1480,11 @@ case "$target_cpu" in then echo "#define USE_KQEMU 1" >> $config_h fi + if test "$kvm" = "yes" ; then + echo "CONFIG_KVM=yes" >> $config_mak + echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak + echo "#define CONFIG_KVM 1" >> $config_h + fi ;; alpha) echo "TARGET_ARCH=alpha" >> $config_mak diff --git a/cpu-defs.h b/cpu-defs.h index 5dcac74c34..46d4487811 100644 --- a/cpu-defs.h +++ b/cpu-defs.h @@ -142,6 +142,9 @@ typedef struct icount_decr_u16 { } icount_decr_u16; #endif +struct kvm_run; +struct KVMState; + #define CPU_TEMP_BUF_NLONGS 128 #define CPU_COMMON \ struct TranslationBlock *current_tb; /* currently executing TB */ \ @@ -199,6 +202,9 @@ typedef struct icount_decr_u16 { /* user data */ \ void *opaque; \ \ - const char *cpu_model_str; + const char *cpu_model_str; \ + struct KVMState *kvm_state; \ + struct kvm_run *kvm_run; \ + int kvm_fd; #endif diff --git a/cpu-exec.c b/cpu-exec.c index 62886a4f18..b3662400ff 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -22,6 +22,7 @@ #include "exec.h" #include "disas.h" #include "tcg.h" +#include "kvm.h" #if !defined(CONFIG_SOFTMMU) #undef EAX @@ -371,6 +372,19 @@ int cpu_exec(CPUState *env1) } #endif + if (kvm_enabled()) { + int ret; + ret = kvm_cpu_exec(env); + if ((env->interrupt_request & CPU_INTERRUPT_EXIT)) { + env->interrupt_request &= ~CPU_INTERRUPT_EXIT; + env->exception_index = EXCP_INTERRUPT; + cpu_loop_exit(); + } else if (env->halted) { + cpu_loop_exit(); + } else + longjmp(env->jmp_env, 1); + } + next_tb = 0; /* force lookup of first TB */ for(;;) { interrupt_request = env->interrupt_request; diff --git a/exec.c b/exec.c index 697a19eb25..ef1072b6dd 100644 --- a/exec.c +++ b/exec.c @@ -39,6 +39,7 @@ #include "tcg.h" #include "hw/hw.h" #include "osdep.h" +#include "kvm.h" #if defined(CONFIG_USER_ONLY) #include #endif @@ -2212,6 +2213,9 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, kqemu_set_phys_mem(start_addr, size, phys_offset); } #endif + if (kvm_enabled()) + kvm_set_phys_mem(start_addr, size, phys_offset); + size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; end_addr = start_addr + (target_phys_addr_t)size; for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { diff --git a/hw/acpi.c b/hw/acpi.c index 45963d37e8..66a5faa0fd 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -23,6 +23,7 @@ #include "sysemu.h" #include "i2c.h" #include "smbus.h" +#include "kvm.h" //#define DEBUG @@ -501,6 +502,12 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s); + if (kvm_enabled()) { + /* Mark SMM as already inited to prevent SMM from running. KVM does not + * support SMM mode. */ + pci_conf[0x5B] = 0x02; + } + /* XXX: which specification is used ? The i82731AB has different mappings */ pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; diff --git a/monitor.c b/monitor.c index 61bd33543a..8fff3aa752 100644 --- a/monitor.c +++ b/monitor.c @@ -37,6 +37,7 @@ #include #include "qemu-timer.h" #include "migration.h" +#include "kvm.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -1263,6 +1264,19 @@ static void do_info_kqemu(void) #endif } +static void do_info_kvm(void) +{ +#ifdef CONFIG_KVM + term_printf("kvm support: "); + if (kvm_enabled()) + term_printf("enabled\n"); + else + term_printf("disabled\n"); +#else + term_printf("kvm support: not compiled\n"); +#endif +} + #ifdef CONFIG_PROFILER int64_t kqemu_time; @@ -1497,6 +1511,8 @@ static const term_cmd_t info_cmds[] = { "", "show dynamic compiler info", }, { "kqemu", "", do_info_kqemu, "", "show kqemu information", }, + { "kvm", "", do_info_kvm, + "", "show kvm information", }, { "usb", "", usb_info, "", "show guest USB devices", }, { "usbhost", "", usb_host_info, diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 263a477765..167bae268a 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -587,6 +587,8 @@ typedef struct CPUX86State { target_ulong kernelgsbase; #endif + uint64_t tsc; + uint64_t pat; /* exception/interrupt handling */ @@ -617,6 +619,10 @@ typedef struct CPUX86State { int kqemu_enabled; int last_io_time; #endif + + /* For KVM */ + uint64_t interrupt_bitmap[256 / 64]; + /* in order to simplify APIC support, we leave this pointer to the user */ struct APICState *apic_state; diff --git a/target-i386/helper.c b/target-i386/helper.c index bcf5e7f705..4b8c503798 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -29,6 +29,7 @@ #include "exec-all.h" #include "svm.h" #include "qemu-common.h" +#include "kvm.h" //#define DEBUG_MMU @@ -115,6 +116,8 @@ CPUX86State *cpu_x86_init(const char *cpu_model) #ifdef USE_KQEMU kqemu_init(env); #endif + if (kvm_enabled()) + kvm_init_vcpu(env); return env; } @@ -1288,6 +1291,40 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) } #endif /* !CONFIG_USER_ONLY */ +#if defined(CONFIG_KVM) +static void host_cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + uint32_t vec[4]; + +#ifdef __x86_64__ + asm volatile("cpuid" + : "=a"(vec[0]), "=b"(vec[1]), + "=c"(vec[2]), "=d"(vec[3]) + : "0"(function) : "cc"); +#else + asm volatile("pusha \n\t" + "cpuid \n\t" + "mov %%eax, 0(%1) \n\t" + "mov %%ebx, 4(%1) \n\t" + "mov %%ecx, 8(%1) \n\t" + "mov %%edx, 12(%1) \n\t" + "popa" + : : "a"(function), "S"(vec) + : "memory", "cc"); +#endif + + if (eax) + *eax = vec[0]; + if (ebx) + *ebx = vec[1]; + if (ecx) + *ecx = vec[2]; + if (edx) + *edx = vec[3]; +} +#endif + void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) @@ -1307,12 +1344,23 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, *ebx = env->cpuid_vendor1; *edx = env->cpuid_vendor2; *ecx = env->cpuid_vendor3; + + /* sysenter isn't supported on compatibility mode on AMD. and syscall + * isn't supported in compatibility mode on Intel. so advertise the + * actuall cpu, and say goodbye to migration between different vendors + * is you use compatibility mode. */ + if (kvm_enabled()) + host_cpuid(0, NULL, ebx, ecx, edx); break; case 1: *eax = env->cpuid_version; *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ *ecx = env->cpuid_ext_features; *edx = env->cpuid_features; + + /* "Hypervisor present" bit required for Microsoft SVVP */ + if (kvm_enabled()) + *ecx |= (1 << 31); break; case 2: /* cache info: needed for Pentium Pro compatibility */ @@ -1390,6 +1438,31 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, *ebx = 0; *ecx = env->cpuid_ext3_features; *edx = env->cpuid_ext2_features; + + if (kvm_enabled()) { + uint32_t h_eax, h_edx; + + host_cpuid(0x80000001, &h_eax, NULL, NULL, &h_edx); + + /* disable CPU features that the host does not support */ + + /* long mode */ + if ((h_edx & 0x20000000) == 0 /* || !lm_capable_kernel */) + *edx &= ~0x20000000; + /* syscall */ + if ((h_edx & 0x00000800) == 0) + *edx &= ~0x00000800; + /* nx */ + if ((h_edx & 0x00100000) == 0) + *edx &= ~0x00100000; + + /* disable CPU features that KVM cannot support */ + + /* svm */ + *ecx &= ~4UL; + /* 3dnow */ + *edx = ~0xc0000000; + } break; case 0x80000002: case 0x80000003: diff --git a/vl.c b/vl.c index ee93c73087..ea9e6529e9 100644 --- a/vl.c +++ b/vl.c @@ -39,6 +39,7 @@ #include "block.h" #include "audio/audio.h" #include "migration.h" +#include "kvm.h" #include #include @@ -4782,6 +4783,9 @@ static void help(int exitcode) "-kernel-kqemu enable KQEMU full virtualization (default is user mode only)\n" "-no-kqemu disable KQEMU kernel module usage\n" #endif +#ifdef CONFIG_KVM + "-enable-kvm enable KVM full virtualization support\n" +#endif #ifdef TARGET_I386 "-no-acpi disable ACPI\n" #endif @@ -4887,6 +4891,7 @@ enum { QEMU_OPTION_pidfile, QEMU_OPTION_no_kqemu, QEMU_OPTION_kernel_kqemu, + QEMU_OPTION_enable_kvm, QEMU_OPTION_win2k_hack, QEMU_OPTION_usb, QEMU_OPTION_usbdevice, @@ -4973,6 +4978,9 @@ static const QEMUOption qemu_options[] = { { "no-kqemu", 0, QEMU_OPTION_no_kqemu }, { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu }, #endif +#ifdef CONFIG_KVM + { "enable-kvm", 0, QEMU_OPTION_enable_kvm }, +#endif #if defined(TARGET_PPC) || defined(TARGET_SPARC) { "g", 1, QEMU_OPTION_g }, #endif @@ -5793,6 +5801,14 @@ int main(int argc, char **argv) case QEMU_OPTION_kernel_kqemu: kqemu_allowed = 2; break; +#endif +#ifdef CONFIG_KVM + case QEMU_OPTION_enable_kvm: + kvm_allowed = 1; +#ifdef USE_KQEMU + kqemu_allowed = 0; +#endif + break; #endif case QEMU_OPTION_usb: usb_enabled = 1; @@ -5928,6 +5944,14 @@ int main(int argc, char **argv) } } +#if defined(CONFIG_KVM) && defined(USE_KQEMU) + if (kvm_allowed && kqemu_allowed) { + fprintf(stderr, + "You can not enable both KVM and kqemu at the same time\n"); + exit(1); + } +#endif + machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */ if (smp_cpus > machine->max_cpus) { fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus " @@ -6229,6 +6253,16 @@ int main(int argc, char **argv) } } + if (kvm_enabled()) { + int ret; + + ret = kvm_init(smp_cpus); + if (ret < 0) { + fprintf(stderr, "failed to initialize KVM\n"); + exit(1); + } + } + machine->init(ram_size, vga_ram_size, boot_devices, ds, kernel_filename, kernel_cmdline, initrd_filename, cpu_model);