#include "kvm/kvm.h" #include "kvm/boot-protocol.h" #include "kvm/cpufeature.h" #include "kvm/read-write.h" #include "kvm/interrupt.h" #include "kvm/mptable.h" #include "kvm/util.h" #include "kvm/mutex.h" #include "kvm/kvm-cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFINE_KVM_EXIT_REASON(reason) [reason] = #reason #define KVM_PID_FILE_PATH "/.kvm-tools/" #define HOME_DIR getenv("HOME") const char *kvm_exit_reasons[] = { DEFINE_KVM_EXIT_REASON(KVM_EXIT_UNKNOWN), DEFINE_KVM_EXIT_REASON(KVM_EXIT_EXCEPTION), DEFINE_KVM_EXIT_REASON(KVM_EXIT_IO), DEFINE_KVM_EXIT_REASON(KVM_EXIT_HYPERCALL), DEFINE_KVM_EXIT_REASON(KVM_EXIT_DEBUG), DEFINE_KVM_EXIT_REASON(KVM_EXIT_HLT), DEFINE_KVM_EXIT_REASON(KVM_EXIT_MMIO), DEFINE_KVM_EXIT_REASON(KVM_EXIT_IRQ_WINDOW_OPEN), DEFINE_KVM_EXIT_REASON(KVM_EXIT_SHUTDOWN), DEFINE_KVM_EXIT_REASON(KVM_EXIT_FAIL_ENTRY), DEFINE_KVM_EXIT_REASON(KVM_EXIT_INTR), DEFINE_KVM_EXIT_REASON(KVM_EXIT_SET_TPR), DEFINE_KVM_EXIT_REASON(KVM_EXIT_TPR_ACCESS), DEFINE_KVM_EXIT_REASON(KVM_EXIT_S390_SIEIC), DEFINE_KVM_EXIT_REASON(KVM_EXIT_S390_RESET), DEFINE_KVM_EXIT_REASON(KVM_EXIT_DCR), DEFINE_KVM_EXIT_REASON(KVM_EXIT_NMI), DEFINE_KVM_EXIT_REASON(KVM_EXIT_INTERNAL_ERROR), }; #define DEFINE_KVM_EXT(ext) \ .name = #ext, \ .code = ext struct { const char *name; int code; } kvm_req_ext[] = { { DEFINE_KVM_EXT(KVM_CAP_COALESCED_MMIO) }, { DEFINE_KVM_EXT(KVM_CAP_SET_TSS_ADDR) }, { DEFINE_KVM_EXT(KVM_CAP_PIT2) }, { DEFINE_KVM_EXT(KVM_CAP_USER_MEMORY) }, { DEFINE_KVM_EXT(KVM_CAP_IRQ_ROUTING) }, { DEFINE_KVM_EXT(KVM_CAP_IRQCHIP) }, { DEFINE_KVM_EXT(KVM_CAP_HLT) }, { DEFINE_KVM_EXT(KVM_CAP_IRQ_INJECT_STATUS) }, { DEFINE_KVM_EXT(KVM_CAP_EXT_CPUID) }, }; extern struct kvm *kvm; extern struct kvm_cpu *kvm_cpus[KVM_NR_CPUS]; static int pause_event; static DEFINE_MUTEX(pause_lock); static bool kvm__supports_extension(struct kvm *kvm, unsigned int extension) { int ret; ret = ioctl(kvm->sys_fd, KVM_CHECK_EXTENSION, extension); if (ret < 0) return false; return ret; } static int kvm__check_extensions(struct kvm *kvm) { unsigned int i; for (i = 0; i < ARRAY_SIZE(kvm_req_ext); i++) { if (!kvm__supports_extension(kvm, kvm_req_ext[i].code)) { pr_error("Unsuppored KVM extension detected: %s", kvm_req_ext[i].name); return (int)-i; } } return 0; } static struct kvm *kvm__new(void) { struct kvm *kvm = calloc(1, sizeof *kvm); if (!kvm) die("out of memory"); return kvm; } static void kvm__create_pidfile(struct kvm *kvm) { int fd; char full_name[PATH_MAX], pid[10]; if (!kvm->name) return; sprintf(full_name, "%s/%s", HOME_DIR, KVM_PID_FILE_PATH); mkdir(full_name, 0777); sprintf(full_name, "%s/%s/%s.pid", HOME_DIR, KVM_PID_FILE_PATH, kvm->name); fd = open(full_name, O_CREAT | O_WRONLY, 0666); sprintf(pid, "%u\n", getpid()); if (write(fd, pid, strlen(pid)) <= 0) die("Failed creating PID file"); close(fd); } void kvm__remove_pidfile(const char *name) { char full_name[PATH_MAX]; sprintf(full_name, "%s/%s/%s.pid", HOME_DIR, KVM_PID_FILE_PATH, name); unlink(full_name); } int kvm__get_pid_by_instance(const char *name) { int fd, pid; char pid_str[10], pid_file[PATH_MAX]; sprintf(pid_file, "%s/%s/%s.pid", HOME_DIR, KVM_PID_FILE_PATH, name); fd = open(pid_file, O_RDONLY); if (fd < 0) return -1; if (read(fd, pid_str, 10) == 0) return -1; pid = atoi(pid_str); if (pid < 0) return -1; return pid; } int kvm__enumerate_instances(void (*callback)(const char *name, int pid)) { char full_name[PATH_MAX]; int pid; DIR *dir; struct dirent entry, *result; sprintf(full_name, "%s/%s", HOME_DIR, KVM_PID_FILE_PATH); dir = opendir(full_name); for (;;) { readdir_r(dir, &entry, &result); if (result == NULL) break; if (entry.d_type == DT_REG) { entry.d_name[strlen(entry.d_name)-4] = 0; pid = kvm__get_pid_by_instance(entry.d_name); callback(entry.d_name, pid); } } return 0; } void kvm__delete(struct kvm *kvm) { kvm__stop_timer(kvm); munmap(kvm->ram_start, kvm->ram_size); kvm__remove_pidfile(kvm->name); free(kvm); } static bool kvm__cpu_supports_vm(void) { struct cpuid_regs regs; u32 eax_base; int feature; regs = (struct cpuid_regs) { .eax = 0x00, }; host_cpuid(®s); switch (regs.ebx) { case CPUID_VENDOR_INTEL_1: eax_base = 0x00; feature = KVM__X86_FEATURE_VMX; break; case CPUID_VENDOR_AMD_1: eax_base = 0x80000000; feature = KVM__X86_FEATURE_SVM; break; default: return false; } regs = (struct cpuid_regs) { .eax = eax_base, }; host_cpuid(®s); if (regs.eax < eax_base + 0x01) return false; regs = (struct cpuid_regs) { .eax = eax_base + 0x01 }; host_cpuid(®s); return regs.ecx & (1 << feature); } /* * Note: KVM_SET_USER_MEMORY_REGION assumes that we don't pass overlapping * memory regions to it. Therefore, be careful if you use this function for * registering memory regions for emulating hardware. */ void kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr) { struct kvm_userspace_memory_region mem; int ret; mem = (struct kvm_userspace_memory_region) { .slot = kvm->mem_slots++, .guest_phys_addr = guest_phys, .memory_size = size, .userspace_addr = (unsigned long)userspace_addr, }; ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem); if (ret < 0) die_perror("KVM_SET_USER_MEMORY_REGION ioctl"); } /* * Allocating RAM size bigger than 4GB requires us to leave a gap * in the RAM which is used for PCI MMIO, hotplug, and unconfigured * devices (see documentation of e820_setup_gap() for details). * * If we're required to initialize RAM bigger than 4GB, we will create * a gap between 0xe0000000 and 0x100000000 in the guest virtual mem space. */ void kvm__init_ram(struct kvm *kvm) { u64 phys_start, phys_size; void *host_mem; if (kvm->ram_size < KVM_32BIT_GAP_START) { /* Use a single block of RAM for 32bit RAM */ phys_start = 0; phys_size = kvm->ram_size; host_mem = kvm->ram_start; kvm__register_mem(kvm, phys_start, phys_size, host_mem); } else { /* First RAM range from zero to the PCI gap: */ phys_start = 0; phys_size = KVM_32BIT_GAP_START; host_mem = kvm->ram_start; kvm__register_mem(kvm, phys_start, phys_size, host_mem); /* Second RAM range from 4GB to the end of RAM: */ phys_start = 0x100000000ULL; phys_size = kvm->ram_size - phys_size; host_mem = kvm->ram_start + phys_start; kvm__register_mem(kvm, phys_start, phys_size, host_mem); } } int kvm__max_cpus(struct kvm *kvm) { int ret; ret = ioctl(kvm->sys_fd, KVM_CHECK_EXTENSION, KVM_CAP_NR_VCPUS); if (ret < 0) die_perror("KVM_CAP_NR_VCPUS"); return ret; } struct kvm *kvm__init(const char *kvm_dev, u64 ram_size, const char *name) { struct kvm_pit_config pit_config = { .flags = 0, }; struct kvm *kvm; int ret; if (!kvm__cpu_supports_vm()) die("Your CPU does not support hardware virtualization"); kvm = kvm__new(); kvm->sys_fd = open(kvm_dev, O_RDWR); if (kvm->sys_fd < 0) { if (errno == ENOENT) die("'%s' not found. Please make sure your kernel has CONFIG_KVM enabled and that the KVM modules are loaded.", kvm_dev); if (errno == ENODEV) die("'%s' KVM driver not available.\n # (If the KVM module is loaded then 'dmesg' may offer further clues about the failure.)", kvm_dev); fprintf(stderr, " Fatal, could not open %s: ", kvm_dev); perror(NULL); exit(1); } ret = ioctl(kvm->sys_fd, KVM_GET_API_VERSION, 0); if (ret != KVM_API_VERSION) die_perror("KVM_API_VERSION ioctl"); kvm->vm_fd = ioctl(kvm->sys_fd, KVM_CREATE_VM, 0); if (kvm->vm_fd < 0) die_perror("KVM_CREATE_VM ioctl"); if (kvm__check_extensions(kvm)) die("A required KVM extention is not supported by OS"); ret = ioctl(kvm->vm_fd, KVM_SET_TSS_ADDR, 0xfffbd000); if (ret < 0) die_perror("KVM_SET_TSS_ADDR ioctl"); ret = ioctl(kvm->vm_fd, KVM_CREATE_PIT2, &pit_config); if (ret < 0) die_perror("KVM_CREATE_PIT2 ioctl"); kvm->ram_size = ram_size; if (kvm->ram_size < KVM_32BIT_GAP_START) { kvm->ram_start = mmap(NULL, ram_size, PROT_RW, MAP_ANON_NORESERVE, -1, 0); } else { kvm->ram_start = mmap(NULL, ram_size + KVM_32BIT_GAP_SIZE, PROT_RW, MAP_ANON_NORESERVE, -1, 0); if (kvm->ram_start != MAP_FAILED) { /* * We mprotect the gap (see kvm__init_ram() for details) PROT_NONE so that * if we accidently write to it, we will know. */ mprotect(kvm->ram_start + KVM_32BIT_GAP_START, KVM_32BIT_GAP_SIZE, PROT_NONE); } } if (kvm->ram_start == MAP_FAILED) die("out of memory"); madvise(kvm->ram_start, kvm->ram_size, MADV_MERGEABLE); ret = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP); if (ret < 0) die_perror("KVM_CREATE_IRQCHIP ioctl"); kvm->name = name; kvm__create_pidfile(kvm); return kvm; } #define BOOT_LOADER_SELECTOR 0x1000 #define BOOT_LOADER_IP 0x0000 #define BOOT_LOADER_SP 0x8000 #define BOOT_CMDLINE_OFFSET 0x20000 #define BOOT_PROTOCOL_REQUIRED 0x206 #define LOAD_HIGH 0x01 static int load_flat_binary(struct kvm *kvm, int fd) { void *p; int nr; if (lseek(fd, 0, SEEK_SET) < 0) die_perror("lseek"); p = guest_real_to_host(kvm, BOOT_LOADER_SELECTOR, BOOT_LOADER_IP); while ((nr = read(fd, p, 65536)) > 0) p += nr; kvm->boot_selector = BOOT_LOADER_SELECTOR; kvm->boot_ip = BOOT_LOADER_IP; kvm->boot_sp = BOOT_LOADER_SP; return true; } static const char *BZIMAGE_MAGIC = "HdrS"; static bool load_bzimage(struct kvm *kvm, int fd_kernel, int fd_initrd, const char *kernel_cmdline, u16 vidmode) { struct boot_params *kern_boot; unsigned long setup_sects; struct boot_params boot; size_t cmdline_size; ssize_t setup_size; void *p; int nr; /* * See Documentation/x86/boot.txt for details no bzImage on-disk and * memory layout. */ if (lseek(fd_kernel, 0, SEEK_SET) < 0) die_perror("lseek"); if (read(fd_kernel, &boot, sizeof(boot)) != sizeof(boot)) return false; if (memcmp(&boot.hdr.header, BZIMAGE_MAGIC, strlen(BZIMAGE_MAGIC))) return false; if (boot.hdr.version < BOOT_PROTOCOL_REQUIRED) die("Too old kernel"); if (lseek(fd_kernel, 0, SEEK_SET) < 0) die_perror("lseek"); if (!boot.hdr.setup_sects) boot.hdr.setup_sects = BZ_DEFAULT_SETUP_SECTS; setup_sects = boot.hdr.setup_sects + 1; setup_size = setup_sects << 9; p = guest_real_to_host(kvm, BOOT_LOADER_SELECTOR, BOOT_LOADER_IP); /* copy setup.bin to mem*/ if (read(fd_kernel, p, setup_size) != setup_size) die_perror("read"); /* copy vmlinux.bin to BZ_KERNEL_START*/ p = guest_flat_to_host(kvm, BZ_KERNEL_START); while ((nr = read(fd_kernel, p, 65536)) > 0) p += nr; p = guest_flat_to_host(kvm, BOOT_CMDLINE_OFFSET); if (kernel_cmdline) { cmdline_size = strlen(kernel_cmdline) + 1; if (cmdline_size > boot.hdr.cmdline_size) cmdline_size = boot.hdr.cmdline_size; memset(p, 0, boot.hdr.cmdline_size); memcpy(p, kernel_cmdline, cmdline_size - 1); } kern_boot = guest_real_to_host(kvm, BOOT_LOADER_SELECTOR, 0x00); kern_boot->hdr.cmd_line_ptr = BOOT_CMDLINE_OFFSET; kern_boot->hdr.type_of_loader = 0xff; kern_boot->hdr.heap_end_ptr = 0xfe00; kern_boot->hdr.loadflags |= CAN_USE_HEAP; kern_boot->hdr.vid_mode = vidmode; /* * Read initrd image into guest memory */ if (fd_initrd >= 0) { struct stat initrd_stat; unsigned long addr; if (fstat(fd_initrd, &initrd_stat)) die_perror("fstat"); addr = boot.hdr.initrd_addr_max & ~0xfffff; for (;;) { if (addr < BZ_KERNEL_START) die("Not enough memory for initrd"); else if (addr < (kvm->ram_size - initrd_stat.st_size)) break; addr -= 0x100000; } p = guest_flat_to_host(kvm, addr); nr = read(fd_initrd, p, initrd_stat.st_size); if (nr != initrd_stat.st_size) die("Failed to read initrd"); kern_boot->hdr.ramdisk_image = addr; kern_boot->hdr.ramdisk_size = initrd_stat.st_size; } kvm->boot_selector = BOOT_LOADER_SELECTOR; /* * The real-mode setup code starts at offset 0x200 of a bzImage. See * Documentation/x86/boot.txt for details. */ kvm->boot_ip = BOOT_LOADER_IP + 0x200; kvm->boot_sp = BOOT_LOADER_SP; return true; } /* RFC 1952 */ #define GZIP_ID1 0x1f #define GZIP_ID2 0x8b static bool initrd_check(int fd) { unsigned char id[2]; if (read_in_full(fd, id, ARRAY_SIZE(id)) < 0) return false; if (lseek(fd, 0, SEEK_SET) < 0) die_perror("lseek"); return id[0] == GZIP_ID1 && id[1] == GZIP_ID2; } bool kvm__load_kernel(struct kvm *kvm, const char *kernel_filename, const char *initrd_filename, const char *kernel_cmdline, u16 vidmode) { bool ret; int fd_kernel = -1, fd_initrd = -1; fd_kernel = open(kernel_filename, O_RDONLY); if (fd_kernel < 0) die("Unable to open kernel %s", kernel_filename); if (initrd_filename) { fd_initrd = open(initrd_filename, O_RDONLY); if (fd_initrd < 0) die("Unable to open initrd %s", initrd_filename); if (!initrd_check(fd_initrd)) die("%s is not an initrd", initrd_filename); } ret = load_bzimage(kvm, fd_kernel, fd_initrd, kernel_cmdline, vidmode); if (initrd_filename) close(fd_initrd); if (ret) goto found_kernel; pr_warning("%s is not a bzImage. Trying to load it as a flat binary...", kernel_filename); ret = load_flat_binary(kvm, fd_kernel); if (ret) goto found_kernel; close(fd_kernel); die("%s is not a valid bzImage or flat binary", kernel_filename); found_kernel: close(fd_kernel); return ret; } /** * kvm__setup_bios - inject BIOS into guest system memory * @kvm - guest system descriptor * * This function is a main routine where we poke guest memory * and install BIOS there. */ void kvm__setup_bios(struct kvm *kvm) { /* standart minimal configuration */ setup_bios(kvm); /* FIXME: SMP, ACPI and friends here */ /* MP table */ mptable_setup(kvm, kvm->nrcpus); } #define TIMER_INTERVAL_NS 1000000 /* 1 msec */ /* * This function sets up a timer that's used to inject interrupts from the * userspace hypervisor into the guest at periodical intervals. Please note * that clock interrupt, for example, is not handled here. */ void kvm__start_timer(struct kvm *kvm) { struct itimerspec its; struct sigevent sev; memset(&sev, 0, sizeof(struct sigevent)); sev.sigev_value.sival_int = 0; sev.sigev_notify = SIGEV_THREAD_ID; sev.sigev_signo = SIGALRM; sev._sigev_un._tid = syscall(__NR_gettid); if (timer_create(CLOCK_REALTIME, &sev, &kvm->timerid) < 0) die("timer_create()"); its.it_value.tv_sec = TIMER_INTERVAL_NS / 1000000000; its.it_value.tv_nsec = TIMER_INTERVAL_NS % 1000000000; its.it_interval.tv_sec = its.it_value.tv_sec; its.it_interval.tv_nsec = its.it_value.tv_nsec; if (timer_settime(kvm->timerid, 0, &its, NULL) < 0) die("timer_settime()"); } void kvm__stop_timer(struct kvm *kvm) { if (kvm->timerid) if (timer_delete(kvm->timerid) < 0) die("timer_delete()"); kvm->timerid = 0; } void kvm__irq_line(struct kvm *kvm, int irq, int level) { struct kvm_irq_level irq_level; irq_level = (struct kvm_irq_level) { { .irq = irq, }, .level = level, }; if (ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) die_perror("KVM_IRQ_LINE failed"); } void kvm__dump_mem(struct kvm *kvm, unsigned long addr, unsigned long size) { unsigned char *p; unsigned long n; size &= ~7; /* mod 8 */ if (!size) return; p = guest_flat_to_host(kvm, addr); for (n = 0; n < size; n += 8) { if (!host_ptr_in_ram(kvm, p + n)) break; printf(" 0x%08lx: %02x %02x %02x %02x %02x %02x %02x %02x\n", addr + n, p[n + 0], p[n + 1], p[n + 2], p[n + 3], p[n + 4], p[n + 5], p[n + 6], p[n + 7]); } } void kvm__pause(void) { int i, paused_vcpus = 0; /* Check if the guest is running */ if (!kvm_cpus[0] || kvm_cpus[0]->thread == 0) return; mutex_lock(&pause_lock); pause_event = eventfd(0, 0); if (pause_event < 0) die("Failed creating pause notification event"); for (i = 0; i < kvm->nrcpus; i++) pthread_kill(kvm_cpus[i]->thread, SIGKVMPAUSE); while (paused_vcpus < kvm->nrcpus) { u64 cur_read; if (read(pause_event, &cur_read, sizeof(cur_read)) < 0) die("Failed reading pause event"); paused_vcpus += cur_read; } close(pause_event); } void kvm__continue(void) { /* Check if the guest is running */ if (!kvm_cpus[0] || kvm_cpus[0]->thread == 0) return; mutex_unlock(&pause_lock); } void kvm__notify_paused(void) { u64 p = 1; if (write(pause_event, &p, sizeof(p)) < 0) die("Failed notifying of paused VCPU."); mutex_lock(&pause_lock); mutex_unlock(&pause_lock); }