| /* |
| * XXX - Credit, licence, copyright |
| */ |
| |
| #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) |
| { |
| return KVM_IOCTL(kvm, KVM_CREATE_VM, (unsigned long)0); |
| } |
| |
| 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_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, ®); |
| } |
| |
| int vcpu_run(int vcpufd, struct kvm_run *run) |
| { |
| for (;;) { |
| KVM_IOCTL(vcpufd, KVM_RUN, NULL); |
| switch (run->exit_reason) { |
| case KVM_EXIT_DEBUG: |
| return KVM_EXIT_DEBUG; |
| case KVM_EXIT_MMIO: |
| { |
| uint64_t payload = *(uint64_t*)(run->mmio.data); /* sorry */ |
| printf("KVM_EXIT_MMIO: addr = 0x%llx, len = %u, is_write = %u, data = 0x%08lx\n", |
| run->mmio.phys_addr, run->mmio.len, run->mmio.is_write, |
| payload); |
| break; |
| } |
| case KVM_EXIT_FAIL_ENTRY: |
| errx(1, "KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%llx", |
| (unsigned long long)run->fail_entry.hardware_entry_failure_reason); |
| case KVM_EXIT_INTERNAL_ERROR: |
| errx(1, "KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x", |
| run->internal.suberror); |
| default: |
| errx(1, "exit_reason = 0x%x", run->exit_reason); |
| } |
| } |
| } |