The typical boilerplate usage will look something like this:
- open("/dev/kvm") -> kvm_fd
- ioctl(kvm_fd, KVM_CREATE_VM) -> vm_fd
- ioctl(vm_fd, KVM_CREATE_VCPU) -> vcpu_fd
- ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, struct vm_memory_region *)
- ...
- ioctl(vcpu_fd, KVM_RUN)
In this POC I wanted to demonstrate how to inspect a VM's state (registers and memory).
To ensure the state doesn't change while we inspect it, we need our code to run after the user mode called the ioctl and before KVM executes the VM.
From this point we can receive the following:
static long kvm_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
{
struct kvm_vcpu *vcpu = filp->private_data; // the vcpu to run
struct kvm *kvm = vcpu->kvm; // the owning virtual machine
...
struct kvm {
...
struct mm_struct *mm; /* userspace tied to this vm */
struct kvm_memslots __rcu *memslots[KVM_ADDRESS_SPACE_NUM]; // KVM_ADDRESS_SPACE_NUM is 1 or 2
struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
atomic_t online_vcpus;
...
}
If KVM_ADDRESS_SPACE_NUM == 2 the second entry references the SMM memory address space. [1]
Each memory slot contains the following:
struct kvm_memory_slot {
gfn_t base_gfn; // guest page number
unsigned long npages;
unsigned long *dirty_bitmap;
struct kvm_arch_memory_slot arch;
unsigned long userspace_addr;
u32 flags;
short id; // a user-provided ID
};
We can read entire pages from the guest using kvm_read_guest_page(), since every memory slot must be page-aligned in address and length:
int __kvm_set_memory_region(struct kvm *kvm, const struct kvm_userspace_memory_region *mem) {
...
if (mem->memory_size & (PAGE_SIZE - 1))
goto out;
if (mem->guest_phys_addr & (PAGE_SIZE - 1))
goto out;
...
}
Another (and probably better) option is to find the relevant page structures via kvm->mm and map them using vmap().
I use __get_user_pages_fast() to find the relevant page structure and vmap() to map these pages to our memory space.
[1] |
[2] | https://lwn.net/Articles/182495/ |