blob: 886e61fb0a03666d88fc7ccbdf0ef28dac141ad4 [file] [log] [blame]
/*
* 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, &region);
}
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) &reg_data;
reg_data = val;
reg.id = reg_id;
KVM_IOCTL(vcpufd, KVM_SET_ONE_REG, &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);
}
}
}