From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Kxks1-0005HK-Mk for qemu-devel@nongnu.org; Wed, 05 Nov 2008 11:04:37 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Kxkrz-0005H8-Q7 for qemu-devel@nongnu.org; Wed, 05 Nov 2008 11:04:36 -0500 Received: from [199.232.76.173] (port=52389 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Kxkrz-0005H5-L0 for qemu-devel@nongnu.org; Wed, 05 Nov 2008 11:04:35 -0500 Received: from savannah.gnu.org ([199.232.41.3]:46633 helo=sv.gnu.org) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1Kxkry-0007Qv-MH for qemu-devel@nongnu.org; Wed, 05 Nov 2008 11:04:35 -0500 Received: from cvs.savannah.gnu.org ([199.232.41.69]) by sv.gnu.org with esmtp (Exim 4.63) (envelope-from ) id 1Kxkry-0006Lo-4b for qemu-devel@nongnu.org; Wed, 05 Nov 2008 16:04:34 +0000 Received: from aliguori by cvs.savannah.gnu.org with local (Exim 4.63) (envelope-from ) id 1Kxkrx-0006Lk-OG for qemu-devel@nongnu.org; Wed, 05 Nov 2008 16:04:33 +0000 MIME-Version: 1.0 Errors-To: aliguori Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Anthony Liguori Message-Id: Date: Wed, 05 Nov 2008 16:04:33 +0000 Subject: [Qemu-devel] [5627] Add KVM support to QEMU Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Revision: 5627 http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=5627 Author: aliguori Date: 2008-11-05 16:04:33 +0000 (Wed, 05 Nov 2008) Log Message: ----------- Add KVM support to QEMU This patch adds very basic KVM support. KVM is a kernel module for Linux that allows userspace programs to make use of hardware virtualization support. It current supports x86 hardware virtualization using Intel VT-x or AMD-V. It also supports IA64 VT-i, PPC 440, and S390. This patch only implements the bare minimum support to get a guest booting. It has very little impact the rest of QEMU and attempts to integrate nicely with the rest of QEMU. Even though this implementation is basic, it is significantly faster than TCG. Booting and shutting down a Linux guest: w/TCG: 1:32.36 elapsed 84% CPU w/KVM: 0:31.14 elapsed 59% CPU Right now, KVM is disabled by default and must be explicitly enabled with -enable-kvm. We can enable it by default later when we have had better testing. Signed-off-by: Anthony Liguori Modified Paths: -------------- trunk/Makefile.target trunk/configure trunk/cpu-defs.h trunk/cpu-exec.c trunk/exec.c trunk/hw/acpi.c trunk/monitor.c trunk/target-i386/cpu.h trunk/target-i386/helper.c trunk/vl.c Modified: trunk/Makefile.target =================================================================== --- trunk/Makefile.target 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/Makefile.target 2008-11-05 16:04:33 UTC (rev 5627) @@ -183,6 +183,9 @@ endif endif +kvm.o: CFLAGS+=$(KVM_CFLAGS) +kvm-all.o: CFLAGS+=$(KVM_CFLAGS) + all: $(PROGS) ######################################################### @@ -581,6 +584,9 @@ 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 Modified: trunk/configure =================================================================== --- trunk/configure 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/configure 2008-11-05 16:04:33 UTC (rev 5627) @@ -115,6 +115,7 @@ nptl="yes" mixemu="no" bluez="yes" +kvm="yes" # OS specific targetos=`uname -s` @@ -303,6 +304,8 @@ ;; --disable-bluez) bluez="no" ;; + --disable-kvm) kvm="no" + ;; --enable-profiler) profiler="yes" ;; --enable-cocoa) @@ -448,6 +451,7 @@ 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" @@ -951,6 +955,30 @@ 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 aio=no @@ -1036,6 +1064,7 @@ 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 @@ 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 @@ 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 @@ 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 Modified: trunk/cpu-defs.h =================================================================== --- trunk/cpu-defs.h 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/cpu-defs.h 2008-11-05 16:04:33 UTC (rev 5627) @@ -142,6 +142,9 @@ } 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 @@ /* 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 Modified: trunk/cpu-exec.c =================================================================== --- trunk/cpu-exec.c 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/cpu-exec.c 2008-11-05 16:04:33 UTC (rev 5627) @@ -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 @@ } #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; Modified: trunk/exec.c =================================================================== --- trunk/exec.c 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/exec.c 2008-11-05 16:04:33 UTC (rev 5627) @@ -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 @@ 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) { Modified: trunk/hw/acpi.c =================================================================== --- trunk/hw/acpi.c 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/hw/acpi.c 2008-11-05 16:04:33 UTC (rev 5627) @@ -23,6 +23,7 @@ #include "sysemu.h" #include "i2c.h" #include "smbus.h" +#include "kvm.h" //#define DEBUG @@ -501,6 +502,12 @@ 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; Modified: trunk/monitor.c =================================================================== --- trunk/monitor.c 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/monitor.c 2008-11-05 16:04:33 UTC (rev 5627) @@ -37,6 +37,7 @@ #include #include "qemu-timer.h" #include "migration.h" +#include "kvm.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -1263,6 +1264,19 @@ #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 @@ "", "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, Modified: trunk/target-i386/cpu.h =================================================================== --- trunk/target-i386/cpu.h 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/target-i386/cpu.h 2008-11-05 16:04:33 UTC (rev 5627) @@ -587,6 +587,8 @@ target_ulong kernelgsbase; #endif + uint64_t tsc; + uint64_t pat; /* exception/interrupt handling */ @@ -617,6 +619,10 @@ 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; Modified: trunk/target-i386/helper.c =================================================================== --- trunk/target-i386/helper.c 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/target-i386/helper.c 2008-11-05 16:04:33 UTC (rev 5627) @@ -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 @@ #ifdef USE_KQEMU kqemu_init(env); #endif + if (kvm_enabled()) + kvm_init_vcpu(env); return env; } @@ -1288,6 +1291,40 @@ } #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 @@ *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 @@ *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: Modified: trunk/vl.c =================================================================== --- trunk/vl.c 2008-11-05 15:34:06 UTC (rev 5626) +++ trunk/vl.c 2008-11-05 16:04:33 UTC (rev 5627) @@ -39,6 +39,7 @@ #include "block.h" #include "audio/audio.h" #include "migration.h" +#include "kvm.h" #include #include @@ -4782,6 +4783,9 @@ "-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 @@ 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 @@ { "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 @@ -5794,6 +5802,14 @@ 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; break; @@ -5928,6 +5944,14 @@ } } +#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 @@ } } + 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);