| |
| #include <err.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/kvm.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include "helpers.h" |
| |
| int get_kvm(void) |
| { |
| int kvm, ret; |
| |
| kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC); |
| if (kvm < 0) |
| err(kvm, "/dev/kvm"); |
| |
| /* Ensure this is the stable version of the KVM API (defined as 12) */ |
| ret = KVM_IOCTL(kvm, KVM_GET_API_VERSION, NULL); |
| if (ret != 12) |
| errx(-EINVAL, "KVM_GET_API_VERSION %d, expected 12", ret); |
| |
| return kvm; |
| } |
| |
| int create_vm(int kvm, bool is_protected) |
| { |
| unsigned long flags = 0; |
| if (is_protected) |
| flags |= 1ul << 31; |
| return KVM_IOCTL(kvm, KVM_CREATE_VM, flags); |
| } |
| |
| int create_vcpu(int kvm, int vmfd, struct kvm_run **run, bool enable_debug) |
| { |
| struct kvm_guest_debug debug = { |
| .control = KVM_GUESTDBG_ENABLE, |
| }; |
| struct kvm_vcpu_init vcpu_init; |
| size_t mmap_size; |
| int vcpufd; |
| |
| /* Create one CPU to run in the VM. */ |
| vcpufd = KVM_IOCTL(vmfd, KVM_CREATE_VCPU, (unsigned long)0); |
| |
| /* Map the shared kvm_run structure and following data. */ |
| mmap_size = KVM_IOCTL(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL); |
| if (mmap_size < sizeof(*run)) |
| err(-ENOMEM, "KVM_GET_VCPU_MMAP_SIZE unexpectedly small"); |
| *run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); |
| if (!*run) |
| err(-ENOMEM, "mmap vcpu"); |
| |
| /* Query KVM for preferred CPU target type that can be emulated. */ |
| KVM_IOCTL(vmfd, KVM_ARM_PREFERRED_TARGET, &vcpu_init); |
| KVM_IOCTL(vcpufd, KVM_ARM_VCPU_INIT, &vcpu_init); |
| |
| if (enable_debug) |
| KVM_IOCTL(vcpufd, KVM_SET_GUEST_DEBUG, &debug); |
| |
| return vcpufd; |
| } |
| |
| void vm_add_mem_page(int vmfd, int slot, uint64_t addr, void *uaddr) |
| { |
| struct kvm_userspace_memory_region region = { |
| .slot = slot, |
| .guest_phys_addr = addr, |
| .userspace_addr = (uint64_t)uaddr, |
| .memory_size = PAGE_SIZE, |
| }; |
| |
| KVM_IOCTL(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion); |
| } |
| |
| void vm_add_mmio_page(int vmfd, int slot, uint64_t addr) |
| { |
| struct kvm_userspace_memory_region region = { |
| .flags = KVM_MEM_READONLY, |
| .slot = slot, |
| .guest_phys_addr = addr, |
| .userspace_addr = 0ULL, |
| .memory_size = PAGE_SIZE, |
| }; |
| |
| KVM_IOCTL(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion); |
| } |
| |
| void set_one_reg(int vcpufd, uint64_t reg_id, uint64_t val) |
| { |
| uint64_t reg_data; |
| struct kvm_one_reg reg; |
| |
| reg.addr = (__u64) ®_data; |
| reg_data = val; |
| reg.id = reg_id; |
| KVM_IOCTL(vcpufd, KVM_SET_ONE_REG, ®); |
| } |
| |
| /* |
| * Local variables: |
| * mode: C |
| * c-file-style: "Linux" |
| * c-basic-offset: 4 |
| * tab-width: 4 |
| * indent-tabs-mode: nil |
| * End: |
| */ |